#include "arbre.h"

void init_Arb(Arbre * arbre, const Regroupement *gpmt)
{
  if(arbre == NULL)
    return;
  arbre->element = creer_Reg();
  copier_Reg(&(arbre->element), gpmt);
  arbre->eFeuille = creer_Ens();
  arbre->eArbre = creer_Ens();
}

void liberer_Arb(Arbre * arbre)
{
  if(arbre)
    {
      int j;
      int i = getTaille_Ens(arbre->eArbre);

      prems_Ens(arbre->eArbre);
      for(j = 0; j < i ; j++)
	{
	  liberer_Arb(getData_Ens(arbre->eArbre));
	  suivant_Ens(arbre->eArbre);
	}
      /*      i = getTaille_Ens(arbre->eFeuille);
      prems_Ens(arbre->eFeuille);
      for(j = 0; j < i; j++)
	{
	  Oeuvre *oeu = getData_Ens(arbre->eFeuille);
	  detruire_Oeu(&oeu);
	  suivant_Ens(arbre->eFeuille);
	  }*/
      
      detruire_Ens(&(arbre->eFeuille), detruire_Oeu);
      
      detruire_Ens(&(arbre->eArbre), NULL);
      
      if(arbre->element != NULL)
	{
	  detruire_Reg(&(arbre->element));
	  arbre->element = NULL;
	}
    }
}

Arbre * creer_Arb(const Regroupement *regroupement)
{
  Arbre * arbre = (Arbre*) malloc (sizeof (Arbre));
  if(arbre)
    init_Arb(arbre, regroupement);
  else
    {
      fprintf(stderr, "Mémoire insuffisant");
      exit(EXIT_FAILURE);
    }
  return arbre;
}

void detruire_Arb(Arbre **arbre)
{ 
  if(arbre && (*arbre))
    {
      if((*arbre)->element != NULL)
	{
	  detruire_Reg(&((*arbre)->element));
	  if((*arbre)->element != NULL)
	    fprintf(stderr, "Destruction du regroupement vient de poser un problème\n");
	  (*arbre)->element = NULL;
	}
      /*      i = getTaille_Ens((*arbre)->eFeuille);
      prems_Ens((*arbre)->eFeuille);
      for(j = 0; j < i; j++)
	{
	  Oeuvre *oeu = getData_Ens((*arbre)->eFeuille);
	  detruire_Oeu(&oeu);
	  suivant_Ens((*arbre)->eFeuille);
	}*/
      detruire_Ens(&((*arbre)->eFeuille), detruire_Oeu);
      /*      i = getTaille_Ens((*arbre)->eArbre);

      prems_Ens((*arbre)->eArbre);
      for(j = 0; j < i ; j++)
	{
	  Arbre *tmp = getData_Ens((*arbre)->eArbre);
	  detruire_Arb(&tmp);
	  suivant_Ens((*arbre)->eArbre);
	  }*/
      detruire_Ens(&((*arbre)->eArbre), detruire_Arb);
      free((*arbre));
    }

  (*arbre) = NULL;
}

Regroupement * getInformation(Arbre *arbre)
{
  if(arbre)
    return arbre->element;
  return NULL;
}

int estVide_Arb(const Arbre * arbre)
{
  if(arbre == NULL)
      return 1;
  if(getTaille_Ens(recupSousArbre_Arb(arbre)) == 0
     && getTaille_Ens(recupFeuille_Arb(arbre)) == 0)
      return 1;
  return 0;
}

Ensemble * recupSousArbre_Arb(const Arbre *arbre)
{
  return arbre->eArbre;
}

Ensemble * recupFeuille_Arb(const Arbre *arbre)
{
  return arbre->eFeuille;
}

void vider_Arb(Arbre * arbre)
{
  libere_Ens(arbre->eArbre, detruire_Arb);
  libere_Ens(arbre->eFeuille, detruire_Oeu);
}

unsigned int hauteur_Arb(const Arbre * arbre)
{
  int a = 0, max = 0, i, tailleEns;
  if(estVide_Arb(arbre))
    return 0;
  else
    {
      tailleEns = getTaille_Ens(arbre->eArbre);
      prems_Ens(arbre->eArbre);
      for(i = 0; i < tailleEns; i++)
	{
	  a = hauteur_Arb(getData_Ens(arbre->eArbre));
	  suivant_Ens(arbre->eArbre);
	  if(a > max)
	    max = a;
	}
     }
  return max + 1;
}

Arbre * recupSousArbreId_Arb(Arbre *arbre, const unsigned int id)
{
  Ensemble *ensArbre;
  int i, tailleEns;
  if(id == getId_Reg(arbre->element))
    return arbre;
  ensArbre = recupSousArbre_Arb(arbre);
  tailleEns = getTaille_Ens(ensArbre);
  prems_Ens(ensArbre);
  for(i = 0; i < tailleEns; i++)
    {
      if(id == getId_Reg(((Arbre *)getData_Ens(ensArbre))->element))
	return getData_Ens(ensArbre);
      else
	{
	  Arbre *arb = recupSousArbreId_Arb(getData_Ens(ensArbre), id);
	  if(arb != NULL)
	    return arb;
	}
      suivant_Ens(ensArbre);
    }
  return NULL;
}

Oeuvre * recupSousFeuilleId_Arb(Arbre *arbre, const unsigned int id)
{
  Ensemble *ensArbre, *ensOeuvre;
  int i, tailleEns;
  ensOeuvre= recupFeuille_Arb(arbre);
  tailleEns = getTaille_Ens(ensOeuvre);
  prems_Ens(ensOeuvre);
  for(i = 0; i < tailleEns; i++)
    {
      
      if(id == getId_Oeu(getData_Ens(ensOeuvre)))
	return getData_Ens(ensOeuvre);
      suivant_Ens(ensOeuvre);
    }
  ensArbre = recupSousArbre_Arb(arbre);
  tailleEns = getTaille_Ens(ensArbre);
  prems_Ens(ensArbre);
  for(i = 0; i < tailleEns; i++)
    {
      Oeuvre *oeuvre;
      oeuvre = recupSousFeuilleId_Arb(getData_Ens(ensArbre), id);
      if(oeuvre != NULL)
	return oeuvre;
      suivant_Ens(ensArbre);
    }
  return NULL;
}

void ajoutNoeud_Arb(Arbre *arbre, Arbre *sousArbre)
{
  if(arbre)
    {
      insert_Ens(arbre->eArbre, sousArbre, NULL, NULL);
    }
}

void ajoutFeuille_Arb(Arbre *arbre, Oeuvre *oeuvre)
{
  if(arbre)
    {
      insert_Ens(arbre->eFeuille, oeuvre, creer_Oeu, copier_Oeu);
    }
}

int possedeArbre_Arb(const Arbre *arbre, const Arbre *arbreChr)
{
  if(arbre && arbreChr)
    {
      int i, j;
      if(arbre == arbreChr)
	return 1;
      i = getTaille_Ens(arbre->eArbre);
      prems_Ens(arbre->eArbre);
      for(j = 0; j < i; j++)
	{
	  if(possedeArbre_Arb(getData_Ens(arbre->eArbre), arbreChr))
	    return 1;
	}
    }
  return 0;
}

void deplacerArbre_Arb(Arbre *arbreDest, Arbre *arbrePrt, Arbre *arbreSrc)
{
  if(arbreDest && arbreSrc)
    {
      if(arbreDest == arbrePrt)
	return;
      if(possedeArbre_Arb(arbreSrc, arbreDest))
	return;
      /* Ajout de l'arbreSrc à l'arbre Dest */
      ajoutNoeud_Arb(arbreDest, arbreSrc);
      /* On enleve arbreSrc de arbrePrt */
      if(arbrePrt)
	{
	  prems_Ens(arbrePrt->eArbre);
	  while(getData_Ens(arbrePrt->eArbre) != arbreSrc && 
		!estDernierElement_Ens(arbrePrt->eArbre))
	    suivant_Ens(arbrePrt->eArbre);
	  if(getData_Ens(arbrePrt->eArbre) == arbreSrc)
	    {
	      suppCourant_Ens(arbrePrt->eArbre, NULL);
	    }
	}
    }
}

void detruireSousArbre_Arb(Arbre *prt, Arbre *detruit)
{
  if(prt && detruit)
    {
      Ensemble *ensemble;
      int taille, i;
      if(prt == detruit)
	return;
      ensemble = recupSousArbre_Arb(prt);
      taille = getTaille_Ens(ensemble);
      prems_Ens(ensemble);
      for(i = 0; i < taille; i++)
	{
	  if(detruit == getData_Ens(ensemble))
	    {
	      /* Destruction de detruit puis on le retire de la liste ou l'inverse c'est mieux :) */
	      suppCourant_Ens(ensemble, detruire_Arb);
	    }
	  else
	    {
	      detruireSousArbre_Arb(getData_Ens(ensemble), detruit);
	    }
	  suivant_Ens(ensemble);
	}
      
    }
}
void deplacerFeuille_Arb(Arbre *arbreDest, Arbre *arbrePrt, Oeuvre *oeuvre)
{
  if(arbreDest && oeuvre)
    {
      /* On enleve l'oeuvre de l'arbre parent */
      if(arbrePrt)
	{
	  prems_Ens(arbrePrt->eFeuille);
	  while(getData_Ens(arbrePrt->eFeuille) != oeuvre && 
		!estDernierElement_Ens(arbrePrt->eFeuille))
	    suivant_Ens(arbrePrt->eFeuille);
	  if(getData_Ens(arbrePrt->eFeuille) == oeuvre)
	    {
	      suppCourant_Ens(arbrePrt->eFeuille, NULL);
	    }
	}
      /* Ajout de l'oeuvre à l'arbre dest */
      ajoutFeuille_Arb(arbreDest, oeuvre);
    }
}

void setMaj_Arb(Arbre *ptr, const char b)
{
  if(ptr)
    {
      ptr->maj = b;
      if(b == 1)
	ptr->sup = !b;
    }
}

char getMaj_Arb(const Arbre *ptr)
{
  if(ptr)
    return ptr->maj;
  return 0;
}

void setSup_Arb(Arbre *ptr, const char b)
{
  if(ptr)
    {
      ptr->sup = b;
      if(b == 1)
	ptr->maj = !b;
    }
}

char getSup_Arb(const Arbre *ptr)
{
  if(ptr)
    return ptr->sup;
  return 0;
}

void afficher_Arb(Arbre *arbre, const int nbTab)
{
  /* On effectue un parcours pour afficher les dossiers en premier puis les fichiers */
  char *titreDossier;
  /*
  char *titreFichier;
  */
  Ensemble *reg, *fic;
  int tailleReg, tailleFic, i, j;
  Oeuvre *oeuvre;

  if(!arbre)
    return;
  printf("Affichage de l'arbre : \n\n");
  titreDossier = getTitre_Reg(getInformation(arbre));
  if(titreDossier != NULL)
    printf("Titre = %s\tID = %d\n", titreDossier, getId_Reg(getInformation(arbre)));
  reg = recupSousArbre_Arb(arbre);
  tailleReg = getTaille_Ens(reg);
  prems_Ens(reg);
  for(i = 0; i < tailleReg; i++)
    {
      for(j = 0; j < nbTab; j++)
	printf("\t");
      afficher_Arb(getData_Ens(reg), nbTab + 1);
      suivant_Ens(reg);
    }
  fic = recupFeuille_Arb(arbre);
  tailleFic = getTaille_Ens(fic);
  prems_Ens(fic);
  for(i = 0; i < tailleFic; i++)
    {
      Ensemble *ensG;
      int k;
      for(j = 0; j < nbTab; j++)
	printf("\t");
      oeuvre = getData_Ens(fic);
      printf("Titre Oeuvre = %s\n", getTitre_Oeu(oeuvre));
      ensG = getEnsembleGenre_Oeu(oeuvre);
      k = getTaille_Ens(ensG);
      prems_Ens(ensG);
      for(j = 0; j <nbTab + 1; j++)
	printf("\t");
      for(j = 0; j < k; j++)
	{
	  printf("%s, ", getLibelle_Gen(getData_Ens(ensG)));
	  suivant_Ens(ensG);
	}
      printf("\n");
      suivant_Ens(fic);
    }
}

void regressionStatic_Arb()
{
  Arbre pere, noeudNv1_1, noeudNv1_2, noeudNv2_1, noeudNv3_1, noeudNv2_2;
  Regroupement rPere, rNoeudNv1_1, rNoeudNv1_2, rNoeudNv2_1, rNoeudNv3_1, rNoeudNv2_2;
  int taille;

  printf("\tTest de regression pour un arbre de ce type :\n");
  printf("pere : id = 0, titre = \"Pere\"\n");
  printf("\tnoeudNv1_1 id = 1, titre = \"Noeud 1\"\n");
  printf("\tnoeudNv1_2 id = 2, titre = \"Noeud 2\"\n");
  printf("\t\tnoeudNv2_1 id = 3, titre = \"Noeud 3 niveau 2 prt Noeud 2\"\n");
  printf("\t\t\tnoeudNv3_1 id = 5, titre = \"Noeud 5 niveau 3 prt Noeud 3\"\n");
  printf("\t\tnoeudNv2_2 id = 4, titre = \"Noeud 4 niveau 2 prt Noeud 2\"\n");
  printf("\n");
  /* Initialisation Regroupement */
  printf("Initialisation des regroupements\n");
  init_Reg(&rPere);
  init_Reg(&rNoeudNv1_1);
  init_Reg(&rNoeudNv1_2);
  init_Reg(&rNoeudNv2_1);
  init_Reg(&rNoeudNv3_1);
  init_Reg(&rNoeudNv2_2);
  printf("Initialisation des titres pour chaque regroupement\n");
  setTitre_Reg(&rNoeudNv1_1, "Noeud 1");
  setTitre_Reg(&rNoeudNv1_2, "Noeud 2");
  setTitre_Reg(&rNoeudNv2_1, "Noeud 3 niveau 2 prt Noeud 2");
  setTitre_Reg(&rNoeudNv2_2, "Noeud 4 niveau 2 prt Noeud 2");
  setTitre_Reg(&rNoeudNv3_1, "Noeud 5 niveau 3 prt Noeud 3");
  printf("Initialisation des ID pour chaque regroupement\n");
  setId_Reg(&rNoeudNv1_1, 1);
  setId_Reg(&rNoeudNv1_2, 2);
  setId_Reg(&rNoeudNv2_1, 3);
  setId_Reg(&rNoeudNv2_2, 4);
  setId_Reg(&rNoeudNv3_1, 5);

  printf("Initisalisation des arbres\n");
  init_Arb(&pere, &rPere);
  init_Arb(&noeudNv1_1, &rNoeudNv1_1);
  init_Arb(&noeudNv1_2, &rNoeudNv1_2);
  init_Arb(&noeudNv2_1, &rNoeudNv2_1);
  init_Arb(&noeudNv3_1, &rNoeudNv3_1);
  init_Arb(&noeudNv2_2, &rNoeudNv2_2);
  
  /* Création de l'arbre pour avoir la structure supèrieur */
  printf("Placement des différents arbres dans le père\n");
  ajoutNoeud_Arb(&pere, &noeudNv1_1);
  ajoutNoeud_Arb(&pere, &noeudNv1_2);
  ajoutNoeud_Arb(&noeudNv1_2, &noeudNv2_1);
  ajoutNoeud_Arb(&noeudNv1_2, &noeudNv2_2);
  ajoutNoeud_Arb(&noeudNv2_1, &noeudNv3_1);

  /* Calcul de la taille */
  printf("Calcul de la hauteur de l'arbre\n");
  taille = hauteur_Arb(&pere);
  if(taille == 3)
    printf("La taille est bien égale à 3\n");
  else
    printf("La taille n'est pas égale à 3 mais à %d\n", taille);
  printf("\n");
  printf("\tDéplacement de noeudNv3_1 pour le mettre dans le père :\n");
  printf("pere : id = 0, titre = \"Pere\"\n");
  printf("\tnoeudNv1_1 id = 1, titre = \"Noeud 1\"\n");
  printf("\tnoeudNv1_2 id = 2, titre = \"Noeud 2\"\n");
  printf("\t\tnoeudNv2_1 id = 3, titre = \"Noeud 3 niveau 2 prt Noeud 2\"\n");
  printf("\tnoeudNv3_1 id = 5, titre = \"Noeud 5 niveau 1 prt Pere\"\n");
  printf("\t\tnoeudNv2_2 id = 4, titre = \"Noeud 4 niveau 2 prt Noeud 2\"\n");
  printf("\n");
  deplacerArbre_Arb(&pere, &noeudNv2_1, &noeudNv3_1);
  printf("Calcul de la hauteur de l'arbre\n");
  taille = hauteur_Arb(&pere);
  if(taille == 2)
    printf("La taille est bien égale à 2\n");
  else
    printf("La taille n'est pas égale à 2 mais à %d\n", taille);

  /* Libération de l'arbre et par la mếme occasion des regroupements contenus dans l'arbre */
  printf("Libération de l'arbre et des regroupements\n");
  liberer_Arb(&pere);

  printf("Libération des regroupements local\n");
  liberer_Reg(&rPere);
  liberer_Reg(&rNoeudNv1_1);
  liberer_Reg(&rNoeudNv1_2);
  liberer_Reg(&rNoeudNv2_1);
  liberer_Reg(&rNoeudNv3_1);
  liberer_Reg(&rNoeudNv2_2);

}

void regressionDynamique_Arb()
{
  Arbre **arbre;
  Regroupement **regroupement;
  int i, nbArbre = 1000;

  /* Initialisation du tableau d'arbre et de regroupement */
  printf("Initialisation du tableau contenant les %d d'arbre et de regroupement\n", nbArbre);
  arbre = (Arbre **) malloc (nbArbre * sizeof(Arbre *));
  regroupement = (Regroupement **) malloc (nbArbre * sizeof(Regroupement *));

  /* Initialisation des nbArbres */
  printf("Initialisation des arbres\n");
  for(i = 0; i < nbArbre; i++)
    {
      regroupement[i] = creer_Reg();
      setId_Reg(regroupement[i], i);
      setTitre_Reg(regroupement[i], "Titre");
      arbre[i] = creer_Arb(regroupement[i]);
    }
  /* Liberation des regroupements que nous avons créer */
  printf("Libération des regroupements  \"static\" du tableau contenant les initialisations\n");
  for(i = 0; i < nbArbre; i++)
    {
      if(regroupement[i] != NULL)
	{
	  detruire_Reg(&regroupement[i]);
	}
    }
  free(regroupement);

  /* Placement des nbArbres sous l'arbre précédent contient l'arbre en cours */
  printf("Placement de l'arbre dans l'arbre précédent\n");
  for(i = 1; i < nbArbre; i++)
    {
      ajoutNoeud_Arb(arbre[i-1], arbre[i]);
    }

  printf("La hauteur de 1000 arbre est de : %d et oui, car le pere des peres possède le niveau 0\n", hauteur_Arb(arbre[0]));

  printf("Déplacement du dernier arbre dans le premier\n");
  deplacerArbre_Arb(arbre[0], arbre[998], arbre[999]);
  printf("La nouvelle taille de l'arbre devrait être de 998 est de %d\n", hauteur_Arb(arbre[0]));
  printf("Déplacement de l'arbre premier (qui contient les autres) vers le dernier\n");
  deplacerArbre_Arb(arbre[998], arbre[0], arbre[1]); 
  vider_Arb(arbre[500]);
  detruire_Arb(&arbre[0]);

  free(arbre);
  printf("Fin du test dynamique \n\n\n");
}
void regression_Arb()
{
  regressionStatic_Arb();
  regressionDynamique_Arb();
}
